#include "precomp.h"
#pragma hdrstop

#include "mrxglobs.h"

#include "ntverp.h"
#include "hwfs_shared.h"

HWFS_STATE g_hwFsState = HWFS_STARTABLE;

#if DBG
UINT64 g_hwFsDebugMask  = DBG_SESSION;
LONG   g_hwFsDebugLevel = DBG_WARN;
#else
UINT64 g_hwFsDebugMask = 0;
LONG   g_hwFsDebugLevel = DBG_ERR;
#endif

//
//  This is the minirdr dispatch table. It is initialized by
//  HwFsInitializeTables. This table will be used by the wrapper to call
//  into this minirdr
//

struct _MINIRDR_DISPATCH  g_hwFsDispatch;

//
// Pointer to the device Object for this minirdr. Since the device object is
// created by the wrapper when this minirdr registers, this pointer is
// initialized in the DriverEntry routine below (see RxRegisterMinirdr)
//

PRDBSS_DEVICE_OBJECT      g_hwFsDeviceObject;

// Current state of the minirdr initialization.
typedef enum _HWFS_INIT_STATES {
    HWFSINIT_ALL_INITIALIZATION_COMPLETED,
    HWFSINIT_MINIRDR_REGISTERED,
    HWFSINIT_START
} HWFS_INIT_STATES;

static
NTSTATUS
HwFsInitializeTables(
    void
);

static
VOID
HwFsInitUnwind(
    IN PDRIVER_OBJECT   DriverObject,
    IN HWFS_INIT_STATES HwFsInitState
);

DRIVER_INITIALIZE DriverEntry;
DRIVER_UNLOAD     HwFsUnloadDriver;
DRIVER_DISPATCH   HwFsFsdDispatch;

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, HwFsInitializeTables)
#pragma alloc_text(PAGE, HwFsUnloadDriver)
#pragma alloc_text(PAGE, HwFsIoCtl)
#pragma alloc_text(PAGE, HwFsInitUnwind)
#endif

NTSTATUS
HwFsIoCtl(
    IN OUT PRX_CONTEXT RxContext
)
{
    UNREFERENCED_PARAMETER(RxContext);

    NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
    RxTraceEnter("HwFsIoCtl");
    RxTraceLeave(Status);
    return Status;
}

static
NTSTATUS
HwFsStartMinirdr(VOID)
{
    NTSTATUS    Status;
    PRX_CONTEXT RxContext;

    RxContext = RxCreateRxContext(
        NULL,
        g_hwFsDeviceObject,
        RX_CONTEXT_FLAG_IN_FSP);
    if (RxContext == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Status = RxStartMinirdr(
        RxContext,
        &RxContext->PostRequest);
    if (Status == STATUS_SUCCESS) {
        HWFS_STATE State;

        State = (HWFS_STATE)InterlockedCompareExchange(
            (LONG*)&g_hwFsState,
            HWFS_STARTED,
            HWFS_STARTABLE);
        if (State != HWFS_STARTABLE) {
            Status = STATUS_REDIRECTOR_STARTED;
            HWFS_DBG_PRINT(DBG_INFO, DBG_ANY, ("STATUS_REDIR_STARTED\n"));
        }

        //
        //  Chance to get resources in context
        //  of system process.....!!!
        //
    } else if (Status == STATUS_PENDING) {
        // Ok
    }

    RxDereferenceAndDeleteRxContext(RxContext);

    return Status;
}


static
NTSTATUS
HwFsStopMinirdr(VOID)
{
    NTSTATUS    Status;
    PRX_CONTEXT RxContext;

    RxContext = RxCreateRxContext(
        NULL,
        g_hwFsDeviceObject,
        RX_CONTEXT_FLAG_IN_FSP);
    if (RxContext == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Status = RxStopMinirdr(
        RxContext,
        &RxContext->PostRequest);
    if (Status == STATUS_SUCCESS) {
        HWFS_STATE state;

        state = (HWFS_STATE)InterlockedCompareExchange(
            (LONG*)&g_hwFsState,
            HWFS_STARTABLE,
            HWFS_STARTED);
    } else if (Status == STATUS_PENDING) {
        // Ok
    }

    RxDereferenceAndDeleteRxContext(RxContext);

    return Status;
}


NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
)
{
    NTSTATUS           Status;
    ULONG              Controls = 0;
    HWFS_INIT_STATES   HwFsInitState = 0;
    UNICODE_STRING     HwFsName;
    UNICODE_STRING     UserModeDeviceName;
    PHWFS_DEVICE_EXTENSION pDeviceExtension;
    ULONG i;

    DbgPrint("+++ HWFS Driver %p Loaded +++\n", DriverObject);

    Status = RxDriverEntry(DriverObject, RegistryPath);
    if (Status != STATUS_SUCCESS) {
        DbgPrint("Wrapper failed to initialize. Status = %08lx\n", Status);
        return(Status);
    }

    try {
        HwFsInitState = HWFSINIT_START;

        //
        //  Register this minirdr with the connection engine. Registration makes the connection
        //  engine aware of the device name, driver object, and other characteristics.
        //  If registration is successful, a new device object is returned
        //
        //

        RtlInitUnicodeString(&HwFsName, DD_HWFS_FS_DEVICE_NAME_U);
        SetFlag(Controls, RX_REGISTERMINI_FLAG_DONT_PROVIDE_MAILSLOTS);

        Status = RxRegisterMinirdr(
            &g_hwFsDeviceObject,                 // where the new device object goes
            DriverObject,                      // the Driver Object to register
            &g_hwFsDispatch,                     // the dispatch table for this driver
            Controls,                          // dont register with unc and for mailslots
            &HwFsName,                         // the device name for this minirdr
            sizeof(HWFS_DEVICE_EXTENSION),     // IN ULONG DeviceExtensionSize,
            FILE_DEVICE_NETWORK_FILE_SYSTEM,   // IN ULONG DeviceType - disk ?
            FILE_REMOTE_DEVICE                 // IN  ULONG DeviceCharacteristics
        );
        if (Status != STATUS_SUCCESS) {
            DbgPrint("HwFsDriverEntry failed: %08lx\n", Status);
            try_return(Status);
        }

        //
        //  Init the device extension data
        //  NOTE: the device extension actually points to fields
        //  in the RDBSS_DEVICE_OBJECT. Our space is past the end
        //  of this struct !!
        //

        pDeviceExtension = (PHWFS_DEVICE_EXTENSION)
            ((PUCHAR)(g_hwFsDeviceObject)+sizeof(RDBSS_DEVICE_OBJECT));

        RxDefineNode(pDeviceExtension, HWFS_DEVICE_EXTENSION);
        pDeviceExtension->DeviceObject = g_hwFsDeviceObject;

        pDeviceExtension->FuseSessionInitialized = FALSE;

        // Mutex for synchronizining our connection list
        ExInitializeFastMutex(&pDeviceExtension->LCMutex);
        InitializeListHead(&pDeviceExtension->ConnectionList);

        // The device object has been created. Need to setup a symbolic
        // link so that the device may be accessed from a Win32 user mode
        // application.

        RtlInitUnicodeString(&UserModeDeviceName, DD_HWFS_USERMODE_SHADOW_DEV_NAME_U);
        Status = IoCreateSymbolicLink(&UserModeDeviceName, &HwFsName);
        if (Status != STATUS_SUCCESS) {
            DbgPrint("HwFsDriverEntry failed: %08lx\n", Status);
            try_return(Status);
        }

        HwFsInitState = HWFSINIT_MINIRDR_REGISTERED;

        //
        // Build the dispatch tables for the minirdr
        //
        Status = HwFsInitializeTables();
        if (!NT_SUCCESS(Status)) {
            try_return(Status);
        }

    try_exit: NOTHING;
    }
    finally {
        if (Status != STATUS_SUCCESS) {
            HwFsInitUnwind(DriverObject, HwFsInitState);
        }
    }

    if (Status != STATUS_SUCCESS) {
        DbgPrint("HwFs failed to start with %08lx %08lx\n", Status, HwFsInitState);
        return Status;
    }

    //
    //  Setup Unload Routine
    //
    DriverObject->DriverUnload = HwFsUnloadDriver;

    //
    //setup the driver dispatch for people who come in here directly....like the browser
    //
    for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
        DriverObject->MajorFunction[i] = (PDRIVER_DISPATCH)HwFsFsdDispatch;
    }

    //
    //  Start the mini-rdr (used to be a START IOCTL)
    //
    Status = HwFsStartMinirdr();
    if (!NT_SUCCESS(Status)) {
        DbgPrint("HwFs: Failed to start minirdr: 0x%08lx\n", Status);
    }

    return  STATUS_SUCCESS;
}

VOID
HwFsInitUnwind(
    IN PDRIVER_OBJECT DriverObject,
    IN HWFS_INIT_STATES HwFsInitState
)
/*++

Routine Description:

     This routine does the common uninit work for unwinding from a bad driver entry or for unloading.

Arguments:

     HwFsInitState - tells how far we got into the intialization

Return Value:

     None

--*/

{
    PAGED_CODE();

    switch (HwFsInitState) {
        case HWFSINIT_ALL_INITIALIZATION_COMPLETED:

            //Nothing extra to do...this is just so that the constant in RxUnload doesn't change.......
            //lack of break intentional

        case HWFSINIT_MINIRDR_REGISTERED:
            RxUnregisterMinirdr(g_hwFsDeviceObject);

            //lack of break intentional

        case HWFSINIT_START:
            break;

        default:
            ASSERT(0); // should not get here
            break;
    }
}

VOID
HwFsUnloadDriver(
    IN PDRIVER_OBJECT DriverObject
)
{
    PRX_CONTEXT RxContext;
    NTSTATUS    Status;
    UNICODE_STRING  UserModeDeviceName;

    PAGED_CODE();

    HwFsInitUnwind(DriverObject, HWFSINIT_ALL_INITIALIZATION_COMPLETED);

    HwFsStopMinirdr();

    RtlInitUnicodeString(&UserModeDeviceName, DD_HWFS_USERMODE_SHADOW_DEV_NAME_U);
    Status = IoDeleteSymbolicLink(&UserModeDeviceName);
    if (Status != STATUS_SUCCESS) {
        DbgPrint("HwFs: Could not delete Symbolic Link\n");
    }

    RxUnload(DriverObject);
    DbgPrint("+++ HWFS Driver %p Unoaded +++\n", DriverObject);
}

NTSTATUS
HwFsInitializeTables(
    void
)
{
    //
    // Build the local minirdr dispatch table and initialize
    //
    ZeroAndInitializeNodeType(&g_hwFsDispatch, RDBSS_NTC_MINIRDR_DISPATCH, sizeof(MINIRDR_DISPATCH));

    //
    // null mini redirector extension sizes and allocation policies.
    //
    g_hwFsDispatch.MRxFlags = (
        RDBSS_MANAGE_NET_ROOT_EXTENSION |
        RDBSS_MANAGE_V_NET_ROOT_EXTENSION |
        RDBSS_MANAGE_FCB_EXTENSION |
        RDBSS_MANAGE_FOBX_EXTENSION |
        RDBSS_MANAGE_SRV_OPEN_EXTENSION);

    g_hwFsDispatch.MRxSrvCallSize = 0; // srvcall extension is not handled in rdbss
    g_hwFsDispatch.MRxNetRootSize = sizeof(HWFS_NETROOT_EXTENSION);
    g_hwFsDispatch.MRxVNetRootSize = 0;
    g_hwFsDispatch.MRxFcbSize = sizeof(HWFS_FCB_EXTENSION);
    g_hwFsDispatch.MRxFobxSize = sizeof(HWFS_FOBX_EXTENSION);
    g_hwFsDispatch.MRxSrvOpenSize = sizeof(HWFS_SRVOPEN_EXTENSION);

    // Mini redirector cancel routine ..

    g_hwFsDispatch.MRxCancel = NULL;

    //
    // Mini redirector Start/Stop. Each mini-rdr can be started or stopped
    // while the others continue to operate.
    //
    g_hwFsDispatch.MRxStart = HwFsStart;
    g_hwFsDispatch.MRxStop = HwFsStop;
    g_hwFsDispatch.MRxDevFcbXXXControlFile = HwFsDevFcbXXXControlFile;

    //
    // Mini redirector name resolution.
    //
    g_hwFsDispatch.MRxCreateSrvCall = HwFsCreateSrvCall;
    g_hwFsDispatch.MRxSrvCallWinnerNotify = HwFsSrvCallWinnerNotify;
    g_hwFsDispatch.MRxCreateVNetRoot = HwFsCreateVNetRoot;
    g_hwFsDispatch.MRxUpdateNetRootState = HwFsUpdateNetRootState;
    g_hwFsDispatch.MRxExtractNetRootName = HwFsExtractNetRootName;
    g_hwFsDispatch.MRxFinalizeSrvCall = HwFsFinalizeSrvCall;
    g_hwFsDispatch.MRxFinalizeNetRoot = HwFsFinalizeNetRoot;
    g_hwFsDispatch.MRxFinalizeVNetRoot = HwFsFinalizeVNetRoot;

    //
    // File System Object Creation/Deletion.
    //
    g_hwFsDispatch.MRxCreate = HwFsCreate;
    g_hwFsDispatch.MRxCollapseOpen = HwFsCollapseOpen;
    g_hwFsDispatch.MRxShouldTryToCollapseThisOpen = HwFsShouldTryToCollapseThisOpen;
    g_hwFsDispatch.MRxExtendForCache = HwFsExtendFile;
    g_hwFsDispatch.MRxExtendForNonCache = HwFsExtendFile;
    g_hwFsDispatch.MRxTruncate = HwFsTruncateFile;
    g_hwFsDispatch.MRxCleanupFobx = HwFsCleanupFobx;
    g_hwFsDispatch.MRxCloseSrvOpen = HwFsCloseSrvOpen;
    g_hwFsDispatch.MRxFlush = HwFsFlush;
    g_hwFsDispatch.MRxForceClosed = HwFsForcedClose;
    g_hwFsDispatch.MRxDeallocateForFcb = HwFsDeallocateForFcb;
    g_hwFsDispatch.MRxDeallocateForFobx = HwFsDeallocateForFobx;

    //
    // File System Objects query/Set
    //
    g_hwFsDispatch.MRxQueryDirectory = HwFsQueryDirectory;
    g_hwFsDispatch.MRxQueryVolumeInfo = HwFsQueryVolumeInformation;
    g_hwFsDispatch.MRxIsValidDirectory = HwFsIsValidDirectory;
    g_hwFsDispatch.MRxQueryEaInfo = HwFsQueryEaInformation;
    g_hwFsDispatch.MRxSetEaInfo = HwFsSetEaInformation;
    g_hwFsDispatch.MRxQuerySdInfo = HwFsQuerySecurityInformation;
    g_hwFsDispatch.MRxSetSdInfo = HwFsSetSecurityInformation;
    g_hwFsDispatch.MRxQueryFileInfo = HwFsQueryFileInformation;
    g_hwFsDispatch.MRxSetFileInfo = HwFsSetFileInformation;
    g_hwFsDispatch.MRxSetFileInfoAtCleanup = HwFsSetFileInformationAtCleanup;

    //
    // Buffering state change
    //
    g_hwFsDispatch.MRxComputeNewBufferingState = HwFsComputeNewBufferingState;

    //
    // File System Object I/O
    //
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_READ] = HwFsRead;
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_WRITE] = HwFsWrite;
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_SHAREDLOCK] = HwFsLocks;
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_EXCLUSIVELOCK] = HwFsLocks;
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_UNLOCK] = HwFsLocks;
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_UNLOCK_MULTIPLE] = HwFsLocks;
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_FSCTL] = HwFsFsCtl;
    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_IOCTL] = HwFsIoCtl;

    g_hwFsDispatch.MRxLowIOSubmit[LOWIO_OP_NOTIFY_CHANGE_DIRECTORY] = HwFsNotifyChangeDirectory;

    //
    // Miscellanous
    //
    g_hwFsDispatch.MRxCompleteBufferingStateChangeRequest = HwFsCompleteBufferingStateChangeRequest;

    return(STATUS_SUCCESS);
}




NTSTATUS
HwFsStart(
    PRX_CONTEXT RxContext,
    IN OUT PRDBSS_DEVICE_OBJECT RxDeviceObject)
{
    return STATUS_SUCCESS;
}


NTSTATUS
HwFsStop(
    PRX_CONTEXT RxContext,
    IN OUT PRDBSS_DEVICE_OBJECT RxDeviceObject)
{
    return STATUS_SUCCESS;
}

NTSTATUS
HwFsFsdDispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp)
{
    NTSTATUS Status = STATUS_SUCCESS;

    RxTraceEnter("HwFsFsdDispatch");

    ASSERT(DeviceObject == (PDEVICE_OBJECT)g_hwFsDeviceObject);
    if (DeviceObject != (PDEVICE_OBJECT)g_hwFsDeviceObject) {
        Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return (STATUS_INVALID_DEVICE_REQUEST);
    }

    Status = RxFsdDispatch((PRDBSS_DEVICE_OBJECT)g_hwFsDeviceObject, Irp);
    return Status;
}

